package Model;

use FileHandle;
use Moose;
use Data::Dumper;
use Data::UUID;

use Log;
use IFile;

has 'root' => (
    is => 'ro',
    isa => 'Str',
    );

has 'modelfile' => (
    is => 'rw',
    isa => 'Str',
    default => 'MODEL.CONF',
    );

has 'filename' => (
    is => 'rw',
    isa => 'Str',
    init_arg => undef,
);

has 'ifile'  => (
    is => 'rw',
    isa => 'IFile', 
    init_arg => undef,    
    );

has 'model' => (
    is => 'rw',
    isa => 'HashRef[ArrayRef]',
    init_arg => undef,
    );

has 'isTerminal' => (
    is => 'rw',
    isa => 'HashRef',
    init_arg => undef,
    default => sub {  { } } ,
    );

has 'isLeaf' => (
    is => 'rw' ,
    isa => 'HashRef',
    init_arg => undef,
    default => sub { { } } ,
    );    

has 'log' => (
    is => 'ro' ,
    isa => 'Log',
    init_arg => undef,
    default => sub { return Log->instance; } ,
);

sub BUILD {
    my $s = shift;
#    my $log = Log->instance;
    if(! -d $s->root) {
	$s->log->xsupererror("a6e04cbc-d8e3-35d1-9110-79fb00ea0a5f",
			     $s->log->format("MODEL","",$s->root." is not a directory"));
    }
    $s->filename($s->root . "/". $s->modelfile);
    if(! -f $s->filename) {
	$s->log->xsupererror("82cc4eeb-6b4e-3351-a5bb-f02497f7e918",
			     $s->log->format("MODEL","",$s->filename." is not a plain file"));
    }
    $s->ifile(IFile->new(filename => $s->filename));
#    $s->read_raw_ifile();
}


sub _old_read_raw_ifile {
    my $self = shift;
    my $fh = FileHandle->new($self->filename,"r");
    $self->log->xsupererror("b0929d96-8b3a-3f5e-a464-c880d730b151",
			    $self->log->format("MODEL","","cannot open ".$self->filename))
	unless ($fh->open($self->filename));
    my $line = 0;
#    my $idata = $self->idata;
    my $what;
    my $current_indent = 0;
    my $indent = 0;
    my $ug = new Data::UUID;
    while(<$fh>) {
	# print STDERR $_;
	chomp;

	$line++;
	if(/^\#/o) {
	    $indent=0;
	    $what = 'comment';
	}elsif(/^[\s\t]*$/o) {
	    $indent=0;
	    $what = 'empty';
	}else{
	    my $s1=length($_);
	    s/^\t+//o;
	    $indent=$s1-length($_);
	    $self->log->xsupererror("7dd0661c-23fd-3e58-868e-3fb755c9f1f6",
				   $self->log->format($self->filename,$line,"indent error (too much indent level)"))
		if($indent > $current_indent+1);
	    s/\s+$//go;
	    $what = 'data';
	    
	}	
	my $rec = {
	    'line' => $line,
	    'value' => $_,
	    'indent' => $indent,
	    'what'   => $what,
	    'guid'   => $ug->create(),
	    'origin' => $self->filename,
	};
	$current_indent = $indent;
	$self->ifile->push($rec);
#	push(@{$self->idata},$rec);
    }
#    $self->idata($idata);
    $fh->close;    
}


sub read_model {
	my $self = shift;
#	$self->read_raw_ifile();
#	my $idata = $self->idata;
#	print STDERR "idata=".Dumper($idata);
	my $model = { };
	my $cur_k ;
	foreach my $i (@{$self->ifile->idata}) {
	   if($i->{'what'} eq 'data') {

		if($i->{'indent'} == 0) {
			$cur_k=$i->{'value'};
			$model->{$cur_k} = [ ];
			$self->_add_isTerminal($i->{'line'},$cur_k,0);
		}elsif($i->{'indent'} == 1) {
		    unless($cur_k) {
		    	$self->log->xsupererror("8700c33b-c9a4-39ae-8bdf-a2febb310467",
					     $self->log->format($self->filename,$i->{'line'},"model error"));
			$self->log->debug(1,"",'$i='.Dumper($i));
		    }
		    my $v = $i->{'value'};
		    my $isTerminal = ($v =~ s/^\*//o) ? 0 : 1;
		    my $rec = {
			'isTerminal' => $isTerminal,
			'value'      => $v,
			'line'       => $i->{'line'},
		    };

		    push(@{$model->{$cur_k}},$rec);
		    $self->_add_isTerminal($i->{'line'},$v,$isTerminal);
		}elsif($i->{'indent'} > 1) {
		    $self->log->xsupererror("9a40ae41-8a19-35f3-8c98-d697278f770b",
					    $self->log->format($self->filename,$i->{'line'},
							       "model error indent"));		    
		}
	   }

	   

	}


	# now check that each non terminal is in the right-part and each terminal
	# is not in a right part:
	# inoltre calcola quali sono le foglie.


	foreach my $r (keys(%$model)) {

	    # assumi che sia una foglia:
	    my $isLeaf = 1;

	    foreach my $i (@{$model->{$r}}) {

		if($i->{'isTerminal'} ne $self->isTerminal->{$i->{'value'}}) {
		    $self->log->xfatal("b4f143d3-518c-35f8-be57-4d2093eeb962",
					    $self->log->format($self->filename,$i->{'line'}," internal error: ".$i->{'value'}." and ".Dumper($i)));
		}

		if($i->{'isTerminal'} == 0) {
		    unless($model->{$i->{'value'}}) {
			$self->log->xsupererror("122dbb0c-2fff-3532-b374-da61d5331c67",
						$self->log->format($self->filename,$i->{'line'},
								   "non terminal do not exists"));		  		    
			$self->log->dumper(1,'$i',$i);
		    }
		    # non appena trovi un non Terminale all'interno di
		    # $r , non e' una foglia:
		    $isLeaf = 0;		    
		}elsif($i->{'isTerminal'} == 1) {
		    if($model->{$i->{'value'}}) {
			$self->log->xsupererror("d92ec1ce-c0ee-3f6d-92da-6f91eafaa27a",
						$self->log->format($self->filename,$i->{'line'},
								   "terminal seems to be non terminal:"));
			$self->log->dumper(1,'$i',$i);
		    }
		    
		}else{
		    $self->log->xfatal("01c33838-b093-3832-9e7c-9959b334f12c",
				       $self->log->format($self->filename,$i->{'line'}," internal error"));
		    
		}
	    }
	    
	    # imposta lo stato foglia:
	    $self->isLeaf->{$r} = $isLeaf;
	}
	$self->model($model);
}

sub _add_isTerminal {
    my $self = shift;
    my $line = shift;
    my $key = shift;
    my $boolValue = shift;

    # check that no identifiers starts with "_":
    $self->log->xsupererror("8d1cf906-5f6a-3f24-aec7-73d6e755437b",
			   $self->log->format($self->filename,$line,"$key : indentifiers starting with '_' are reserved for internal use")) 
	if($key =~ /^_/o);
    

    if(exists($self->isTerminal->{$key})) {
	$self->log->xsupererror("a4cd1148-2118-3c95-abe9-e94b77bbf457",
				$self->log->format($self->filename,"",
						   "isTerimal is not consistent for '$key'"))
	    if($self->isTerminal->{$key} ne $boolValue);
    }else{
	$self->isTerminal->{$key} = $boolValue;
    }
}

sub check_isTerminal {
    my $self = shift;
    my $key = shift;
    return undef unless(exists($self->isTerminal->{$key}));
    return $self->isTerminal->{$key};
}
sub check_isLeaf {
    my $self = shift;
    my $key = shift;
    return undef unless($self->exists($key));
    return $self->isLeaf->{$key};
}

sub dump {
    my $self=shift;
    
    print STDERR Dumper($self->model);
    print STDERR Dumper($self->isTerminal);
    print STDERR Dumper($self->isLeaf);
}

sub exists {
    my $self = shift;
    my $key = shift;
    return exists( $self->model->{$key} ) ? 1 : 0 ;
}



no Moose;
__PACKAGE__->meta->make_immutable;
